/*D1、D2屏幕SCL\SDA，D3门，D4灯，D5门继电器，D7蜂鸣器，D6指纹继电器，D8指纹蓝色线，3V3绿色，RX，TX分别接指纹TX，RX，交叉的*/
/*用到的库：Adafruit_Fingerprint,Adafruit_GFX,Adafruit_SSD1306,blinker Arduino库( https://diandeng.tech/doc/001-%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B%2F02-esp8266-WiFi%E6%8E%A5%E5%85%A5.md )*/
/*可修改行：
 * 30~32：接入的路由器帐号密码blinker密钥修改
 * 34~35：ESP8266创建的热点名和密码
 * 110：网页的标题
 * 118~136：开门代码
 * 459~460：ESP8266创建的热点名的IP地址
 */
/*BLINKER，小爱同学等库函数*/
#define BLINKER_PRINT Serial
#define BLINKER_WIFI
#define BLINKER_MIOT_LIGHT
#include <Blinker.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
/*************************************/
/*指纹模块库函数，申明*/
#include <Adafruit_Fingerprint.h>
#define mySerial Serial
Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);
Adafruit_SSD1306 display(128, 64, &Wire);
/**********************/
/************************修改******************************/
char auth[] = "";//你的密钥
char ssid[] = ""; //要接入的WIFI名
char pswd[] = ""; //接入的密码


const char* AP_ssid ="604物联网主控";//要创建的热点名
const char* AP_password ="yyh666666";//创建的热点密码
/*********************************************************/
//取16X16汉字字模 逐行式 顺向高位在前
static const unsigned char PROGMEM str0[] =
{
  0x00,0x00,0x7F,0xFC,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0xFF,0xFE,
  0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x10,0x20,0x10,0x20,0x20,0x20,0x40,0x20};/*"开",0*/
static const unsigned char PROGMEM str1[] =
{
  0x20,0x00,0x13,0xFC,0x10,0x04,0x40,0x04,0x40,0x04,0x40,0x04,0x40,0x04,0x40,0x04,
  0x40,0x04,0x40,0x04,0x40,0x04,0x40,0x04,0x40,0x04,0x40,0x04,0x40,0x14,0x40,0x08};/*"门",1*/
 static const unsigned char PROGMEM str2[] =
{
  0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x3F,0xF8,0x21,0x08,0x21,0x08,0x21,0x08,
  0x21,0x08,0x21,0x08,0x3F,0xF8,0x21,0x08,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00};/*"中",2*/
static const unsigned char PROGMEM str3[] =
{
  0x11,0x00,0x11,0x04,0x11,0x38,0x11,0xC0,0xFD,0x02,0x11,0x02,0x10,0xFE,0x14,0x00,
  0x19,0xFC,0x31,0x04,0xD1,0x04,0x11,0xFC,0x11,0x04,0x11,0x04,0x51,0xFC,0x21,0x04};/*"指",3*/
static const unsigned char PROGMEM str4[] =
{
  0x10,0x80,0x10,0x40,0x20,0x40,0x27,0xFC,0x49,0x10,0xF1,0x10,0x11,0x10,0x21,0x10,
  0x41,0x10,0xF8,0xA0,0x40,0xA0,0x00,0x40,0x18,0xA0,0xE1,0x10,0x42,0x08,0x0C,0x06};/*"纹",4*/
static const unsigned char PROGMEM str5[] =
{
  0x21,0x10,0x21,0x10,0x39,0x10,0x27,0xFC,0x41,0x10,0x79,0x10,0xAF,0xFE,0x20,0x00,
  0xFB,0xF8,0x22,0x08,0x22,0x08,0x23,0xF8,0x2A,0x08,0x32,0x08,0x23,0xF8,0x02,0x08};/*"错",5*/
static const unsigned char PROGMEM str6[] =
{
  0x00,0x00,0x43,0xF8,0x22,0x08,0x22,0x08,0x03,0xF8,0x00,0x00,0xE0,0x00,0x27,0xFC,
  0x20,0x40,0x20,0x40,0x2F,0xFE,0x20,0x40,0x28,0xA0,0x31,0x10,0x22,0x08,0x0C,0x06};/*"误",6*/
static const unsigned char PROGMEM str7[] =
{
  0x00,0x40,0x40,0x40,0x27,0xFC,0x20,0x40,0x03,0xF8,0x00,0x40,0xE7,0xFE,0x20,0x00,
  0x23,0xF8,0x22,0x08,0x23,0xF8,0x22,0x08,0x2B,0xF8,0x32,0x08,0x22,0x28,0x02,0x10};/*"请",7*/
static const unsigned char PROGMEM str8[] =
{
  0x08,0x00,0x08,0x00,0x08,0x00,0x1F,0xFC,0x12,0x44,0x22,0x44,0x42,0x44,0x84,0x44,
  0x04,0x44,0x08,0x84,0x10,0x84,0x21,0x04,0x42,0x04,0x04,0x04,0x08,0x28,0x10,0x10};/*"勿",8*/
static const unsigned char PROGMEM str9[] =
{
  0x08,0x20,0x1C,0x20,0xF0,0x7C,0x10,0x84,0x11,0x48,0xFC,0x30,0x10,0x20,0x30,0x48,
  0x39,0x90,0x54,0x3E,0x54,0x42,0x91,0xA4,0x10,0x18,0x10,0x10,0x10,0x60,0x11,0x80};/*"移",9*/
static const unsigned char PROGMEM str10[] =
{
  0x00,0x10,0x00,0xF8,0x3F,0x00,0x01,0x00,0x01,0x00,0x3F,0xF8,0x01,0x00,0x01,0x00,
  0x01,0x00,0xFF,0xFE,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x05,0x00,0x02,0x00};/*"手",10*/
  
static const unsigned char PROGMEM str19[] =
{
  0x07,0xE0,0x1F,0xF8,0x78,0x1E,0xE0,0x07,0x80,0x01,0x03,0xC0,0x0F,0xF0,0x3C,0x3C,
  0x30,0x0C,0x00,0x00,0x01,0x80,0x07,0xE0,0x06,0x60,0x00,0x00,0x01,0x80,0x01,0x80};/*"wifi已连接图标",19*/
static const unsigned char PROGMEM str20[] =
{
  0x07,0xE3,0x1F,0xFF,0x78,0x1E,0xE0,0x1F,0x80,0x39,0x03,0xF0,0x0F,0xF0,0x3D,0xFC,
  0x33,0x8C,0x07,0x00,0x0F,0x80,0x1F,0xE0,0x3E,0x60,0x70,0x00,0xE1,0x80,0xC1,0x80};/*"wifi未连接图标",20*/
static const unsigned char PROGMEM str21[] =
{
  0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x01,0xC0,0x03,0xE0,0x07,0x70,0x0E,0x38,0x1C,
  0x1C,0x38,0x0E,0x70,0xFF,0xFF,0x80,0x01,0xA0,0x19,0xBC,0x19,0x80,0x01,0xFF,0xFF};/*"路由器图标",21*/
static const unsigned char PROGMEM str22[] =
{
  0xFF,0x00,0xBD,0x10,0xA5,0x18,0xA5,0x08,0xA5,0x4C,0xBD,0x64,0x81,0x24,0x81,0x24,
  0x81,0x24,0x81,0x24,0x81,0x64,0x81,0x4C,0x81,0x08,0x81,0x18,0x81,0x10,0xFF,0x00};/*"板子图标",22*/
/***************BLINKER新建组件对象*****************/
BlinkerButton Button1("door");
BlinkerButton Button2("on");
BlinkerButton Button3("off");
BlinkerNumber Number1("num-abc");

int counter = 0;
/*****************************************************/

/*********网页服务，网页代码***********////////////////////////////下面这个623数字↓↓可以改成你们宿舍名字，这个是网页的标题，不影响使用
ESP8266WebServer server(80);
String html="<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><title>604&#x7684;&#x7269;&#x8054;&#x7F51;&#x63A7;&#x5236;&#x754C;&#x9762;</title></head><body><a href=\"./pin?door=on\"><input type=\"button\"style=\"width: 1000px;height: 500px;font-size:200px;\"value=\"&#x5F00;&#x95E8;\"></a><br/><br/><br/><a href=\"./pin?light=on\"><input type=\"button\"style=\"width: 500px;height: 500px;font-size:200px;\"value=\"&#x5F00;&#x706F;\"></a><a href=\"./pin?light=off\"><input type=\"button\"style=\"width: 500px;height: 500px;font-size:200px;\"value=\"&#x5173;&#x706F;\"></a></body></html>";
/*************************************/
//注意，为达到精确控制，开门，开灯关灯过程中，CPU都会被占用，请勿重复按开门，否则会开两次
/*****************************开门****************************/
void door(){    
      digitalWrite(D5, HIGH);//接通继电器
      delay(500);
      
      for(int i=0;i<280;i++){//拉门，产生PWM，括号中260为持续时间，大约为2.73秒，既一个单位为0.0105秒，让舵机顺时针转   260数值可根据自己门以及舵机力量，给舵机的电压等调整，比如电压低就需要久一点，舵机堵转转矩小也需要久一点，下面同理
      digitalWrite(D3, HIGH);
      delayMicroseconds(500);
      digitalWrite(D3, LOW);
      delay(10);
      }
      
      for(int i=0;i<50;i++){//产生PWM，50为持续时间，大约为0.575秒，让舵机停止，可不改
      digitalWrite(D3, HIGH);
      delayMicroseconds(1500);
      digitalWrite(D3, LOW);
      delay(10);
      }
      
      for(int i=0;i<200;i++){//门恢复，产生PWM，180为持续时间，大约为2.25秒，既一个单位为0.0125秒，让舵机逆时针转
      digitalWrite(D3, HIGH);
      delayMicroseconds(2500);
      digitalWrite(D3, LOW);
      delay(10);
      }
      
      for(int i=0;i<50;i++){//产生PWM，50为持续时间，大约为0.575秒，让舵机停止，可不改
      digitalWrite(D3, HIGH);
      delayMicroseconds(1500);
      digitalWrite(D3, LOW);
      delay(10);
      }
      
      digitalWrite(D5, LOW);//关闭继电器
}
/*************************************************************/
/************开灯*************/
void light_on(){
      for(int i=0;i<50;i++){
      digitalWrite(D4, HIGH);
      delayMicroseconds(1800);
      digitalWrite(D4, LOW);
      delay(10);
      }
      for(int i=0;i<50;i++){
      digitalWrite(D4, HIGH);
      delayMicroseconds(1500);
      digitalWrite(D4, LOW);
      delay(10);
      }
}
/****************************/
/*******关灯*******/
void light_off(){
      for(int i=0;i<50;i++){
      digitalWrite(D4, HIGH);
      delayMicroseconds(1000);
      digitalWrite(D4, LOW);
      delay(10);
      }
      for(int i=0;i<50;i++){
      digitalWrite(D4, HIGH);
      delayMicroseconds(1500);
      digitalWrite(D4, LOW);
      delay(10);
      }
}
/*****************/
/****蜂鸣器长响一声****/
void beer_one(){
//      display.clearDisplay();//清屏
//      display.drawBitmap(36, 24, str0, 16, 16, 1); //开
//      display.drawBitmap(52, 24, str1, 16, 16, 1); //门
//      display.drawBitmap(68, 24, str2, 16, 16, 1); //中
//      display.display();//开显示 
//      digitalWrite(D7, HIGH); 
//      delay(500);  
//      digitalWrite(D7, LOW);
}
/*********************/
/****蜂鸣器长响两声****/
void beer_two(){
//      display.clearDisplay();//清屏
//      display.drawBitmap(30, 24, str3, 16, 16, 1); //指
//      display.drawBitmap(46, 24, str4, 16, 16, 1); //纹
//      display.drawBitmap(62, 24, str5, 16, 16, 1); //错
//      display.drawBitmap(78, 24, str6, 16, 16, 1); //误
//      display.display();//开显示 
//      digitalWrite(D7, HIGH);
//      delay(150); 
//      digitalWrite(D7, LOW); 
//      delay(150); 
//      digitalWrite(D7, HIGH);
//      delay(150); 
//      digitalWrite(D7, LOW); 
}
/*********************/
/*蜂鸣器响三声*/
void beer_three(){
//      display.clearDisplay();//清屏
//      display.drawBitmap(20, 24, str7, 16, 16, 1); //请
//      display.drawBitmap(36, 24, str8, 16, 16, 1); //勿
//      display.drawBitmap(52, 24, str9, 16, 16, 1); //移
//      display.drawBitmap(68, 24, str0, 16, 16, 1); //开
//      display.drawBitmap(84, 24, str10, 16, 16, 1); //手
//      display.drawBitmap(100, 24, str3, 16, 16, 1); //指
//      display.display();//开显示 
//      digitalWrite(D7, HIGH);
//      delay(150); 
//      digitalWrite(D7, LOW); 
//      delay(150); 
//      digitalWrite(D7, HIGH);
//      delay(150); 
//      digitalWrite(D7, LOW); 
//      delay(150); 
//      digitalWrite(D7, HIGH);
//      delay(150); 
//      digitalWrite(D7, LOW); 
}
/************/
/******************************************网页传送回来的代码判断处理****************************/
void pin(){
  if(server.arg("light")=="on"){
    server.send(200, "text/html", html);
      light_on();
    }
    else if(server.arg("light")=="off"){
     server.send(200, "text/html", html);
      light_off();
    }
      else if(server.arg("door")=="on"){
      server.send(200, "text/html", html);
      beer_one();//蜂鸣器长响一声
      door();
    }
}
const int led = 13;


void handleRoot() {
  server.send(200, "text/html", html);
}

void handleNotFound(){
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET)?"GET":"POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i=0; i<server.args(); i++){
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
}
/*************************************************************************************************/


/**************BLINKER按下按键即会执行该函数***************/
void button1_callback(const String & state) {
    BLINKER_LOG("get button state: ", state);
    beer_one();
    door();
}
void button2_callback(const String & state) {
    BLINKER_LOG("get button state: ", state);
    light_on();
}
void button3_callback(const String & state) {
    BLINKER_LOG("get button state: ", state);
    light_off();
}
/********************************************************/

/*BLINKER如果未绑定的组件被触发，则会执行其中内容*/
void dataRead(const String & data)
{
    BLINKER_LOG("Blinker readString: ", data);
    counter++;
    Number1.print(counter);
}
/**********************************************/
/***小爱电源类回调***/
void miotPowerState(const String & state)
{
    BLINKER_LOG("need set power state: ", state);

    if (state == BLINKER_CMD_ON) {
        //digitalWrite(LED_BUILTIN, LOW); 
        BlinkerMIOT.powerState("on");
        BlinkerMIOT.print();
        light_on();  
    }
    else if (state == BLINKER_CMD_OFF) {
        //digitalWrite(LED_BUILTIN, HIGH);
        BlinkerMIOT.powerState("off");
        BlinkerMIOT.print();
        light_off();
    }
}
/*******************/
/**********************小爱模式回调*******************/
void miotMode(uint8_t mode)
{
    BLINKER_LOG("need set mode: ", mode);

    BlinkerMIOT.mode(mode);
    BlinkerMIOT.print();
    if (mode == BLINKER_CMD_MIOT_DAY) {
        // Your mode function
    }
    else if (mode == BLINKER_CMD_MIOT_NIGHT) {
        // Your mode function
    }
    else if (mode == BLINKER_CMD_MIOT_COLOR) {
        // Your mode function
    }
    else if (mode == BLINKER_CMD_MIOT_WARMTH) {
        // Your mode function
    }
    else if (mode == BLINKER_CMD_MIOT_TV) {
        beer_one();               //蜂鸣器长响一声
//        door();
    }
    else if (mode == BLINKER_CMD_MIOT_READING) {
        // Your mode function
    }
    else if (mode == BLINKER_CMD_MIOT_COMPUTER) {
        // Your mode function
    }

//    wsMode = mode;
}
/********************************************************/
void CAM()
{
  int a;
  a=digitalRead(D0);
  if(a==1){
//    door();
  }
}
/***************指纹识别************/
void Press_FR()
{
//  char ensure,i;
//  char str[20];
//  int k;
//  k=digitalRead(D8);
//  if(k==1)
//  {                 //指纹触摸是否输出高电平？
//    digitalWrite(D6, HIGH); //是，则把D6继电器输出高电平，点亮指纹识别
//    delay(300);
//      for(i=0;i<5;i++)
//    {
//      ensure=finger.getImage();
//      if(ensure==0x00)            //获取图像成功 
//        {   
//          ensure=finger.image2Tz();
//          if(ensure==0x00)          //生成特征成功
//          {     
//            ensure=finger.fingerFastSearch();
//            if(ensure==0x00)       //搜索成功
//            { 
//              Serial.print("finger OK");
//              beer_one();                  //蜂鸣器长响一声，识别正确
//              digitalWrite(D6, LOW);//关闭指纹识别
//              door();                      //开门
//              break;
//            }
//            else 
//            {      
//              Serial.print("finger ERROR");//搜索失败
//              beer_two();         //蜂鸣器响两声，识别错误
//              break;
//            }                 
//          }
//          else
//            {                   //获取图像失败
//            }
//        }
//     delay(100);
//      if(i==4)beer_three();//最后一次循环都没有检测到任何指纹，无论是否匹配，蜂鸣器向三声，获取图像失败
//    }
//    delay(500);
//    digitalWrite(D6, LOW);//关闭指纹识别
//    delay(500);
//  }
}
/***********************************/
/****************OLED显示***************/
void oleddisplay(){
    IPAddress APIP = WiFi.softAPIP();
    IPAddress IP = WiFi.localIP();
    IPAddress NAME = WiFi.localIP();
    int16_t year = Blinker.year();
    int8_t month = Blinker.month();
    int8_t mday = Blinker.mday();
    int8_t hour = Blinker.hour();
    int8_t min = Blinker.minute();
    int8_t sec = Blinker.second();

    display.clearDisplay();//清屏
    if(sec==-1){
    display.setCursor(0, 56);//设置显示位置
    display.println("----------");//输出字符
    
    display.setCursor(80, 56);//设置显示位置
    display.println("--:--:--");//输出字符
    }
    else{
    display.setCursor(0, 56);//设置显示位置
    display.println(year);
    display.setCursor(24, 56);//设置显示位置
    display.println("-");//输出字符
    display.setCursor(30, 56);//设置显示位置
    display.println(month);
    display.setCursor(42, 56);//设置显示位置
    display.println("-");//输出字符
    display.setCursor(48, 56);//设置显示位置
    display.println(mday);
    
    display.setCursor(80, 56);//设置显示位置
    display.println(hour);
    display.setCursor(91, 56);//设置显示位置
    display.println(":");//输出字符
    display.setCursor(97, 56);//设置显示位置
    display.println(min);
    display.setCursor(108, 56);//设置显示位置
    display.println(":");//输出字符
    display.setCursor(114, 56);//设置显示位置
    display.println(sec);
    }
    
    display.setCursor(20, 0);//设置显示位置
    display.println("WiFi connect:");//输出字符
    if(WiFi.status()!=WL_CONNECTED){
      display.drawBitmap(0, 0, str20, 16, 16, 1); //wifi连接失败图标
      display.setCursor(20, 9);//设置显示位置
      display.println("fail");//输出字符
    }
    else{
      display.drawBitmap(0, 0, str19, 16, 16, 1); //wifi连接成功图标
      display.setCursor(20, 9);//设置显示位置
      display.println(ssid);//输出字符
    }

    display.drawBitmap(0, 16, str21, 16, 16, 1); //路由器图标
    display.setCursor(20, 18);//设置显示位置
    display.println("WiFi IP Address:");//输出字符
    display.setCursor(20, 26);//设置显示位置
    display.println(IP);//输出字符
    display.drawBitmap(0, 34, str22, 16, 16, 1); //板子图标
    display.setCursor(20, 35);//设置显示位置
    display.println("AP IP Address:");//输出字符
    display.setCursor(20, 44);//设置显示位置
    display.println(APIP);//输出字符
    
    display.display();//开显示 
}
/*********************************************/
void setup() {
/***********初始化串口和IO**********/
    display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
    display.setTextColor(WHITE);//开像素点发光
    display.clearDisplay();//清屏
    Serial.begin(9600);
    finger.begin(9600);
//    pinMode(D8, INPUT);
//    pinMode(D7, OUTPUT);  
//    pinMode(D6, OUTPUT);
    pinMode(D5, OUTPUT);
    pinMode(D4, OUTPUT);
    pinMode(D3, OUTPUT);
//  pinMode(D2, OUTPUT);
//  pinMode(D1, OUTPUT);
    pinMode(D0, INPUT);

//  digitalWrite(D8, LOW);
//    digitalWrite(D7, LOW);
//    digitalWrite(D6, LOW);
    digitalWrite(D5, LOW);
    digitalWrite(D4, LOW);
    digitalWrite(D3, LOW);
//  digitalWrite(D2, LOW);
//  digitalWrite(D1, LOW);
    digitalWrite(D0, LOW);
/***********************************/


/****************初始化网页*******************/
    server.on("/", handleRoot);
    server.on("/pin", HTTP_GET, pin);
    server.on("/inline", [](){
    server.send(200, "text/plain", "this works as well");
    });
    server.onNotFound(handleNotFound);
    server.begin();
    Serial.println("HTTP server started");
 /******************************************/
 /******************初始化blinker********************/
    #if defined(BLINKER_PRINT)
        BLINKER_DEBUG.stream(BLINKER_PRINT);
    #endif

    Blinker.begin(auth, ssid, pswd);
    Blinker.attachData(dataRead);
    Button1.attach(button1_callback);
    Button2.attach(button2_callback);
    Button3.attach(button3_callback);
    BlinkerMIOT.attachPowerState(miotPowerState);//小爱调节电源
    BlinkerMIOT.attachMode(miotMode);
    Blinker.setTimezone(8.0);
/****************************************************/
/***********初始化AP设置**********/
    WiFi.begin();
    Serial.println();
    Serial.print("Setting soft-AP ... ");
       
    IPAddress softLocal(192,168,1,1);  //这里
    IPAddress softGateway(192,168,1,1);//和这里，修改创建热点的接入IP
    IPAddress softSubnet(255,255,255,0);
    WiFi.softAPConfig(softLocal, softGateway, softSubnet);  
    WiFi.softAP(AP_ssid, AP_password);
       
    IPAddress myIP = WiFi.softAPIP();
    Serial.print("AP IP address: ");
    Serial.println(myIP);
/**********************************/
}
void loop() {
    oleddisplay();
    Blinker.run();
    server.handleClient();
    Press_FR();
    CAM();
}
